"""
author: liuxu
date: 2024.12.3
description: 测试用例基类
"""
import os
import re
import json
import shutil
import pathlib
import pandas as pd
import pytest
import glob
import csv
from collections import defaultdict


from common.logger_ctrl import mylogger
from common.ssh_command import exec_cmd
from common.common_func import extract_csv_data, pretty_print
from common.os_utils import OSUtils
from setting import PROJECT_ROOT, DTK_PATH, TMP_DIR, EXE_DIR, CPP_DIR
from base_public.cpp_compile import compile_cpp_file
from base_public.prof_file_data_type import PMCIndicatorType




# hipprof --hip-trace输出文件列表
trace_file_d = {
    "db_filename" : "hip-prof-PID.db",
    "json_filename" : "result_PID.json",
    "hiptrace_filename" : "result_PID.trace.csv",
    "hip_kernel_filename" : "result_PID.kernel.csv",
    "rccl_trace_filename": "result_PID.rccltrace.csv",
    "rccl_kernel_filename": "result_PID.rcclkernel.csv",
}


class BasePublic(OSUtils):
    """
    定义测试基类
    """
    rocm_path = os.environ.get("ROCM_PATH", "/opt/dtk")
    proj_path = PROJECT_ROOT
    src_path = CPP_DIR
    tmp_dir = TMP_DIR  # 存放hipprof输出结果
    exe_dir = EXE_DIR
    _dcu_info = None  # 类属性缓存结果

    @classmethod
    def setup_class(cls):
        pass

    @classmethod
    def teardown_class(cls):
        pass

    # +++++++++++++++++++++++++++++++++++++++++
    # 通用公共函数
    # +++++++++++++++++++++++++++++++++++++++++
    @pytest.fixture(autouse=True)
    def inject_request(self, request):
        """自动注入request对象"""
        self._request = request

    @property
    def dcu_info(self):
        """懒加载dcu_info，首次访问时调用fixture"""
        if self._dcu_info is None:
            self._dcu_info = self._request.getfixturevalue('dcu_info')
        return self._dcu_info

    @staticmethod
    def compile_source_code(src_file_name, des_exe_name=None, compile_paras="", compiler="dcc"):
        """
        编译源码文件
        :param src_file_name: 待编译源码文件名/相对路径
        :param des_exe_name: 生成的可执行文件名称默认去掉后缀
        :param compile_paras: 额外增加的编译参数
        :param compiler: 使用的编译器，默认dcc
        :return:
        """
        # 获取源文件和目标文件全路径
        cpp_abspath = os.path.join(CPP_DIR, src_file_name)
        cpp_basename = os.path.basename(cpp_abspath)
        des_exe_name = des_exe_name if des_exe_name else cpp_basename.rsplit(".", 1)[0]
        exe_abspath = os.path.join(EXE_DIR, des_exe_name)
        # 忽略编译过程无返回值警告
        compile_paras += " -Wno-return-type"

        # 检测可执行文件是否已存在，存在则跳过编译
        if not os.path.exists(exe_abspath):
            if not compile_cpp_file(cpp_abspath, exe_abspath, *compile_paras.split(" "), compiler=compiler):
                pytest.fail(f"{cpp_abspath} compilation failed, Skip the next step")
        return exe_abspath

    def compile_omp_case(self, src_case_path="omp_case/target_data_device.f90"):
        """
        编译omp用例，omp默认只用这1条用例
        :param src_case_path:
        :return:
        """
        exe_case = src_case_path.rsplit(".", 1)[0]
        dcu_arch = self.dcu_info["gfx_arch"]
        compile_paras = f"-B /usr/lib/gcc/x86_64-kylin-linux/12/ -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa -Xopenmp-target=amdgcn-amd-amdhsa -march={dcu_arch}"
        return self.compile_source_code(src_file_name=src_case_path, des_exe_name=exe_case, compile_paras=compile_paras, compiler="flang")

    @staticmethod
    def convert_size_to_human_readable(size_bytes: int) -> str:
        """
        将字节大小转换为类似 `ll -h` 的可读格式（如 1.5K, 200M, 4.2G）
        """
        units = ["B", "K", "M", "G", "T", "P"]
        if size_bytes == 0:
            return "0B"

        # 计算单位索引
        index = 0
        size = float(size_bytes)
        while size >= 1024 and index < len(units) - 1:
            size /= 1024
            index += 1

        # 格式化输出：B单位不保留小数，其他单位保留2位小数
        return f"{size:.2f}{units[index]}" if index > 0 else f"{int(size)}B"

    def convert_pmc_value(self, indicator_value):
        """
        智能转换pmc数值类型：
        1. 处理带括号的单位（如 "0.019040(s)"）
        2. 保留十六进制值（如 "0x0"）
        3. 转换浮点数和整数
        4. 无法转换时返回原始字符串
        """
        # 处理带括号的单位
        if '(' in indicator_value and ')' in indicator_value:
            indicator_value = indicator_value.split('(')[0].strip()

        # 处理十六进制
        if indicator_value.startswith('0x'):
            return int(indicator_value, 16)

        # 尝试数值转换
        try:
            return float(indicator_value) if '.' in indicator_value else int(indicator_value)
        except ValueError:
            return indicator_value  # 保持原始字符串

    @staticmethod
    def exist_file(file_pattern):
        """
        在当前工作路径递归模糊匹配文件是否存在
        :param file_pattern:
        :return:
        """
        return len(glob.glob(file_pattern, recursive=True)) > 0

    @staticmethod
    def get_sys_max_pid():
        """
        获取linux系统的最大进程ID
        :return:
        """
        # max_pid = 65536  # 或32768
        with open('/proc/sys/kernel/pid_max', 'r') as f:
            max_pid = int(f.read().strip())
        return max_pid

    @staticmethod
    def format_str_print(data: [dict, json]):
        """
        格式化字典/json数据，以便打印的美观
        :param data:
        :return:
        """
        return pretty_print(data)

    # +++++++++++++++++++++++++++++++++++++++++
    # 从hipprof打印的trace内容中提取关键信息
    # +++++++++++++++++++++++++++++++++++++++++
    @staticmethod
    def get_prof_pid(output_info):
        """
        获取hipprof运行程序的pid
        :param output_info:
        :return:
        """
        pid_match = re.search(r"HIP_PROF:process\s+id\s+'(\d+)'", output_info)
        return int(pid_match.group(1))

    def get_trace_filename_d(self, output_info):
        pass

    def get_hip_trace_info_from_output(self, output_info) -> dict:
        """
        获取api和kernel的调用信息
        :param output_info:
        :return: 结构化的trace信息字典
        """
        # 获取trace信息，以字典格式返回
        trace_info = {"pid": 0, # hipprof运行程序的pid
                      "cost_time": 0,  # hipprof运行时间，单位：s
                      "api_stat":{},  # hip api调用次数信息
                      "api_total_calls": 0,  # hip api总调用次数
                      "kernel_stat": {},  # kernel 调用次数信息{kernel_name：{paras：[z,y,x],[]}, Calls: 11, AverageNs:time}
                      "kernel_total_calls": 0,  # kernel 总调用次数
                      "dump_json_num": 0  # 打印进度条json数目
                      }
        # -------------------------- 1. 先验证是否包含有效统计信息（兼容高低dtk版本） --------------------------
        # 正则判断程序是否正常运行
        valid_patterns = [
            r"HIP[_ ]PROF:.*HIP API statistics",  # 匹配HIP API统计行
            r"HIP[_ ]PROF:.*HIPOPS API statistics"  # 匹配Kernel对应的HIPOPS统计行
        ]
        if not any(re.search(pattern, output_info, re.IGNORECASE) for pattern in valid_patterns):
            mylogger.warning("The terminal output data is abnormal: no valid HIP/HIPOPS statistics found")
            return trace_info
        # -------------------------- 2. 提取核心基础信息（PID、运行耗时、dump json数） --------------------------
        # 2.1 提取PID（匹配"HIP_PROF:process id '5581'"格式）
        pid_match = re.search(r"HIP[_ ]PROF:process id '(\d+)'", output_info)
        trace_info["pid"] = int(pid_match.group(1)) if pid_match else 0

        # 2.2 提取运行耗时（修正原"totol"拼写错误，匹配"process end run total cost:0(s)"）
        cost_match = re.search(r"HIP[_ ]PROF:process end run total cost:(\d+)\(s\)", output_info)
        trace_info["cost_time"] = int(cost_match.group(1)) if cost_match else -1

        # 2.3 提取dump json总数（匹配"HIP_PROF:dump json trace (194996/194996)"）
        dump_match = re.findall(
            r"HIP[_ ]PROF:dump json(?: trace)? \((\d+)/\d+\)\s+HIP_PROF:finish", output_info)
        trace_info["dump_json_num"] = int(dump_match[-1]) if dump_match else -1
        # assert f'{trace_info["dump_json_num"]}/{trace_info["dump_json_num"]}' in output_info, "The dump json data is inconsistent"

        # -------------------------- 3. 初始化提取标记与正则（兼容两种前缀+忽略IP） --------------------------
        start_api_extracting = False  # HIP API提取标记
        start_kernel_extracting = False  # Kernel（HIPOPS）提取标记

        # 正则：匹配HIP API统计起始行（忽略IP和空格/下划线差异）
        api_start_pattern = re.compile(r"HIP[_ ]PROF:.*HIP API statistics", re.IGNORECASE)
        # 正则：匹配Kernel（HIPOPS）统计起始行（忽略IP和空格/下划线差异）
        kernel_start_pattern = re.compile(r"HIP[_ ]PROF:.*(?:HIPOPS|HIP_Kernel) API statistics", re.IGNORECASE)

        # -------------------------- 4. 逐行提取API/Kernel详情（处理字段空格+索引安全） --------------------------
        for line in output_info.splitlines():
            line = line.strip()
            if not line or line.startswith("---"):
                continue
            if api_start_pattern.search(line):
                # 找到 API 统计信息的起始行，开始提取
                start_api_extracting = True
                continue
            if  kernel_start_pattern.search(line):
                # 停止 API 提取，开始 kernel 提取
                start_api_extracting = False
                start_kernel_extracting = True
                continue
            # (1) 提取每个hip API名称及对应的调用次数，平均耗时
            if start_api_extracting and line.startswith("|") and "Name" not in line and "Total" not in line:
                api_name, calls, total_time, agv_time, percentage = line.strip("|").split("|")
                trace_info["api_stat"][api_name.strip()] = {"calls": int(calls), "agv_time": int(agv_time)}

            # (2) 提取API总调用次数
            if start_api_extracting and line.startswith("|") and "Name" not in line and "Total" in line:
                trace_info["api_total_calls"] = int(line.strip("|").split("|")[1])

            # (3) 提取每个kernel名称及对应的调用次数，平均耗时
            if start_kernel_extracting and line.startswith("|") and "Name" not in line and "Total" not in line:
                if len(line.strip("|").split("|")) == 6:
                    kernel_name, paras, calls, total_time, agv_time, percentage = line.strip("|").split("|")
                else:
                    paras = ""
                    kernel_name, calls, total_time, agv_time, percentage = line.strip("|").split("|")
                trace_info["kernel_stat"][kernel_name.strip()] = {"calls": int(calls), "paras": paras.strip(),"agv_time": int(agv_time)}

            # (4) 提取kernel总调用次数
            if start_kernel_extracting and line.startswith("|") and "Name" not in line and "Total" in line:
                trace_info["kernel_total_calls"] = int(line.strip("|").split("|")[-4])
        # -------------------------- 5. 日志输出与返回 --------------------------
        mylogger.debug(f"trace_info:\n {json.dumps(trace_info, indent=4, ensure_ascii=False)}")
        return trace_info

    @staticmethod
    def get_leak_func_info_form_output(output) -> list:
        """
        从hipprof输出中获取内存泄漏函数信息
        :param output:
        :return: [[fun_name1,times,total_size,device_type],···]
        """
        # 匹配主机/设备端内存泄漏
        p1 = re.compile(r"HIP_PROF:\s+fun_name:(\w+)\s+times:(\d+)\s+total_size:(\d+(?:\.\d+)?\w+)\s+device_type:(\w+)",
                        re.MULTILINE)
        matches1 = re.findall(p1, output.split("leak check start")[-1])
        # 匹配流/事件等资源泄漏
        p2 = re.compile(r"HIP_PROF:\s+fun_name:(\w+)\s+times:(\d+)\s*\n",
                        re.MULTILINE)
        matches2 = re.findall(p2, output.split("leak check start")[-1])
        return matches1 + matches2

    # +++++++++++++++++++++++++++++++++++++++++
    # 获取hipprof输出文件信息
    # +++++++++++++++++++++++++++++++++++++++++
    def get_all_prof_files_info(self, output_info, data_dir="."):
        """
        获取输出目录下所有类型的性能文件信息
        :param output_info: hipprof终端打印信息
        :param data_dir: hipprof工作目录/性能数据文件输出目录
        :return:
        """
        pid = self.get_prof_pid(output_info)
        gen_files = pathlib.Path(data_dir).rglob(f"*{pid}*")  # 通过进程号获取hipprof生成的文件
        # 只有生成的json和html有可能存在多个文件，其他文件路径名都是固定的
        trace_file_d = dict()
        trace_file_d["json_list"] = []
        trace_file_d["html_list"] = []
        # files = os.listdir(data_dir)  # 查看工作目录下文件列表
        mylogger.debug(f"hipprof output data files({pathlib.Path(data_dir).resolve()})：\n{os.listdir(data_dir)}")
        for f in gen_files:
            if f.is_file():
                # 获取文件属性信息
                tmp_file_info = {"name": str(f.name), "abspath": str(f.resolve()), "size": self.convert_size_to_human_readable(f.stat().st_size)}
                if f.name.endswith(".db"):
                    trace_file_d["db"] = tmp_file_info
                # --hip-trace
                elif f.name.endswith(".hiptrace.csv"):
                    trace_file_d["hip_api_csv"] = tmp_file_info
                elif f.name.endswith(".kernel.csv") or f.name.endswith(".hipkernel.csv"):
                    trace_file_d["hip_kernel_csv"] = tmp_file_info
                # --omp-trace
                elif f.name.endswith(".omptrace.csv"):
                    trace_file_d["omp_api_csv"] = tmp_file_info
                elif f.name.endswith(".ompkernel.csv"):
                    trace_file_d["omp_kernel_csv"] = tmp_file_info
                # --rccl-trace
                elif f.name.endswith(".rccltrace.csv"):
                    trace_file_d["rccl_api_csv"] = tmp_file_info
                elif f.name.endswith(".rcclkernel.csv"):
                    trace_file_d["rccl_kernel_csv"] = tmp_file_info
                # --libc-trace
                elif f.name.endswith(".libctrace.csv"):
                    trace_file_d["libc_api_csv"] = tmp_file_info
                # --hsa-trace
                elif f.name.endswith(".hsatrace.csv"):
                    trace_file_d["hsa_api_csv"] = tmp_file_info
                # --kernel-stack
                elif f.name.endswith(".stack.csv") or f.name.endswith(".hipstack.csv"):
                    trace_file_d["kernel_stack_csv"] = tmp_file_info
                elif f.name.endswith(".json"):
                    trace_file_d["json_list"].append(tmp_file_info)
                elif f.name.endswith(".html"):
                    trace_file_d["html_list"].append(tmp_file_info)
                else:
                    raise Exception(f"{f.name} matching failed")
        mylogger.info(f"prof output file info {json.dumps(trace_file_d, indent=4)}")
        return trace_file_d

    @staticmethod
    def get_trace_info_from_csv(csv_path) -> dict:
        """
        从result_pid.*.csv获取api/kernel调用信息
        :param csv_path:
        :return:字典结构，键为CSV中"Name"列的值（即API或Kernel名称），值为包含该名称对应统计信息的子字典。
                 子字典包含以下字段：
                 - "Calls": int类型，调用次数
                 - "TotalDurationNs": float类型，总耗时（单位：纳秒）
                 - "AverageNs": float类型，平均耗时（单位：纳秒）
                 - "Percentage": float类型，耗时占比（百分比）
        """
        # pd_data = pd.DataFrame(pd.read_csv(csv_path))
        # total_calls = int(pd_data["Calls"].iloc[-1])
        result_dict = {}
        with open(csv_path, "r") as f:
            reader = csv.DictReader(f)
            # | Name | Calls | TotalDurationNs | AverageNs | Percentage |
            for row in reader:  # csv的最后一行为total汇总数据
                name = row["Name"]
                result_dict[name] = {
                    "Calls": int(row["Calls"]),
                    "TotalDurationNs": float(row["TotalDurationNs"]),
                    "AverageNs": float(row["AverageNs"]),
                    "Percentage": float(row["Percentage"])
                }
        return result_dict


    @staticmethod
    def get_kernel_stack_info_from_csv(csv_path) -> dict:
        """
        从result_PID.stack.csv获取kernel调用堆栈信息
        :param csv_path:
        :return:
        """
        result_dict = {}
        kernel_cnt = 0  # kernel计数
        with open(csv_path, "r") as f:
            reader = csv.DictReader(f)
            # pid,index,str_name,stack_sq,addr,addrname
            for row in reader:
                if int(row['stack_sq']) == 1:
                    kernel_cnt += 1
                    result_dict[kernel_cnt] = []

                mylogger.debug(result_dict)
                result_dict[kernel_cnt].append({
                    'pid': row['pid'],
                    'index': row['index'],
                    'function': row['str_name'],
                    'stack_sequence': row['stack_sq'],
                    'address': row['addr'],
                    'address_name': row['addrname']
                })
        return result_dict

    @staticmethod
    def parse_prof_json(json_path) -> list:
        """
        解析prof的json文件，提取traceEvents数组数据
        :param json_path:
        :param check:
        :return:
        """
        try:
            # 以只读模式打开 JSON 文件，使用 UTF-8 编码
            with open(json_path, 'r', encoding='utf-8') as file:
                # 尝试使用 json.load 方法解析文件内容
                data = json.load(file)
                return data["traceEvents"]  # 返回数据列表
        except json.JSONDecodeError:  # 检查json格式是否完整
            mylogger.error(f"{json_path} file format error")
        except FileNotFoundError:
            mylogger.error(f"{json_path} does not exist")
        except Exception as e:
            mylogger.error(f"Unknown error occurred：{e}")
        return []

    def get_pmc_info_from_txt(self, pmc_file_path, pmc_type="basic") -> list:
        """
        解析pmc输出的性能数据文件
        :param pmc_file_path: pmc_result_PID.txt文件路径
        :param pmc_type:解析类型，必须是 "basic", "read", 或 "write" 中的一个
        :return:[{kernel1_name:{kernel_dispatch_index: x, gpu_id:x,···}}, {{kernel2_name:{kernel_dispatch_index: x, gpu_id:x,···}}}]
        """
        pmc_info = list()
        # 读取txt文件内容
        file_info = self.read_text_file(pmc_file_path).split("\n")

        def get_pmc_metric() -> dict:
            """获取pmc指标字段名称（空格已改为下划线）"""
            common_metric = PMCIndicatorType.PMC_GENERAL_METRIC
            if "basic" in pmc_type:
                pmc_metric = {**common_metric, **PMCIndicatorType.BASIC_METRIC}
            elif "read" in pmc_type:
                pmc_metric = {**common_metric, **PMCIndicatorType.READ_METRIC}
            elif "write" in pmc_type:
                pmc_metric = {**common_metric, **PMCIndicatorType.WRITE_METRIC}
            else:
                raise "unknown pmc_type"
            return pmc_metric

        cur_kernel_name = None
        cur_kernel_data = None
        for line in file_info:
            line = line.strip()  # 移除行首尾空白字符
            if len(line) == 0:
                if cur_kernel_data:  # 遇到空行则表示当前kernel的数据匹配完成，开始匹配下一个kernel
                    pmc_info.append({cur_kernel_name: cur_kernel_data})
                    cur_kernel_data = None  # 重置下一个内核数据
                continue

            if line.startswith("kernel-name:"):
                cur_kernel_name = line.split(":")[-1].replace('"', '')  # 核函数名称
                # 初始化当前需匹配的kernel数据格式
                cur_kernel_data = {metric: None for metric in get_pmc_metric()}
                continue

            # 处理每个pmc指标数据
            # mylogger.debug(line)
            key, value = line.rsplit(" ", 1)
            key = key.strip().replace(" ", "_")
            value = self.convert_pmc_value(value)

            # 将键值对数据添加至kernel字典
            if key == "fbarrier_count":  # dtk-2510之后已去除此指标
                continue
            if key not in cur_kernel_data:
                raise ValueError(f"This '{key}' data processing is abnormal")
            cur_kernel_data[key] = value

        mylogger.debug(f"The parsed pmc data information:\n{pmc_info}")
        return pmc_info

    def get_pmc_original_data_from_txt(self, pmc_file_path, pmc_type="basic") -> list:
        """解析PMC性能监测数据"""
        dispatches = []
        current_dispatch = None

        file_info = self.read_text_file(pmc_file_path).split("\n")

        for line in file_info:
            # 检测新的dispatch块
            if line.startswith('dispatch['):
                if current_dispatch:
                    dispatches.append(current_dispatch)

                parts = re.findall(r'(\w+[-\w]*)\s*\(([^)]*)\)', line)
                current_dispatch = {
                    'dispatch_index': int(re.search(r'dispatch\[(\d+)\]', line).group(1)),
                    'kernel_name': re.search(r'kernel-name\("([^"]+)"\)', line).group(1)
                }

                for key, value in parts:
                    if key == 'kernel-name':  # 已经单独处理
                        continue
                    try:
                        # 尝试转换为数字
                        current_dispatch[key] = int(value) if value.isdigit() else value
                    except:
                        current_dispatch[key] = value

            # 解析性能计数器
            elif line.strip() and current_dispatch is not None:
                match = re.match(r'\s*([\w\[\]]+)\s*\(([^)]*)\)', line)
                if match:
                    counter = match.group(1)
                    value_str = match.group(2)

                    # 处理数组类型的计数器 (如TA_FLAT_READ_WAVEFRONTS[0])
                    if '[' in counter and ']' in counter:
                        base_name, index = re.match(r'([^\[]+)\[(\d+)\]', counter).groups()
                        index = int(index)

                        if base_name not in current_dispatch:
                            current_dispatch[base_name] = defaultdict(dict)

                        try:
                            value = int(value_str) if value_str.isdigit() else value_str
                        except:
                            value = value_str

                        current_dispatch[base_name][index] = value
                    else:
                        try:
                            value = int(value_str) if value_str.isdigit() else value_str
                        except:
                            value = value_str
                        current_dispatch[counter] = value

        if current_dispatch:
            dispatches.append(current_dispatch)
        mylogger.debug(f"The parsed pmc data information:\n{dispatches}")
        return dispatches

    def get_pmc_info_from_csv(self, csv_path) -> list:
        """
        获取pmc信息
        :param csv_path:
        :return:
        """
        # 读取csv文件内容
        data_list = extract_csv_data(csv_path)

        # 将指标结果由str改为int/float类型
        def convert_record(record):
            return {k: self.convert_pmc_value(v) for k, v in record.items()}
        data_list = list(map(convert_record, data_list))
        mylogger.debug(f"The parsed pmc data information:\n{data_list}")
        return data_list


    # +++++++++++++++++++++++++++++++++++++++++
    # 对hipprof终端输出的内容进行基本检查
    # +++++++++++++++++++++++++++++++++++++++++
    @staticmethod
    def check_help_info(output_info):
        """
        检查hipprof输出帮助信息是否正确
        :param output_info:
        :return:
        """
        return "hipprof [options] <app command line>" in output_info

    def check_trace_data_is_empty(self, output_info):
        """
        检测打印数据是否为空
        :param output_info:
        :return: 为空则返回成功
        """
        trace_info = self.get_hip_trace_info_from_output(output_info)
        return trace_info["api_total_calls"] == 0 and trace_info["kernel_total_calls"] == 0

    def check_trace_output_success(self, output_info):
        """
        检查--hip-trace输出内容是否准确（只做基本检查）
        :param output_info: 终端打印内容
        :return:
        """
        trace_info = self.get_hip_trace_info_from_output(output_info)
        # trace_files= self.get_all_prof_files_info(output_info, data_path)
        # 断言hip api、kernel调用次数均大于1
        return trace_info["api_total_calls"] > 3 and trace_info["kernel_total_calls"] >= 1

    def check_leakcheck_is_success(self, output, cwd="."):
        """
        检测cpp程序和leak-check输出是否无异常输出
        :param output:
        :param cwd:
        :return:
        """
        is_success = True
        # 检测有无异常打印
        # pattern = r' error| fail'
        # if bool(re.search(pattern, output.lower())):
        #     is_success = False
        #     mylogger.error("The leak-check output includes error prints")

        # 检查是否打印内存检测开始、结束标志
        if not re.search(r'HIP[ _]PROF:leak check start.*?HIP_PROF:finish', output, re.DOTALL):
            is_success = False
            mylogger.error("The result of leak-check did not detect the start and end flags")

        # 检测是否生成内存泄漏堆栈文件和内存使用曲线的html文件
        prof_pid = self.get_prof_pid(output)
        html_name_l = [f_info.get("name") for f_info in self.get_all_prof_files_info(output, cwd)["html_list"]]
        if f"result_{prof_pid}.leakcheck.html" not in html_name_l:
            is_success = False
            mylogger.error("The leakcheck stack file was not generated")

        expect_file_l = [f"result_{prof_pid}.leakcheck.html", re.compile(fr"result_{prof_pid}.mem\d+.html")]
        assert len(html_name_l) >=2  # 内存泄漏堆栈文件只有1个，内存使用曲线文件可能有多个
        for f_name in html_name_l:
            for expect_file in expect_file_l:
                if re.fullmatch(expect_file, f_name):
                    expect_file_l.remove(expect_file)
                    break
            else:
                pass # 不匹配的html文件
        if len(expect_file_l) > 0:
            is_success = False
            mylogger.error(f"Not found html file for {expect_file_l}")
        return is_success

    def check_sqtt_is_success(self, output, check_gen_files=True, cwd="."):
        """
        检测sqtt执行是否成功，无异常打印，检测点：
        1、sqtt正常执行结束，打印"HIP_PROF:finish
        2、没有sqtt匹配失败打印
        3、有打印分析se数据
        :param output: 终端打印信息
        :param check_gen_files: 是否检测生成的文件
        :param cwd: 当前工作路径（输出文件目录）
        :return:
        """
        check_result = True  # 初始化检测结果
        # 1、检测终端是否有异常打印
        if "HIP_PROF:finish" not in output:
            mylogger.error(f"The program has not been executed completely. Please check if it has crashed")
            return False
        if re.search("assembly check fail|can not match|error", output, re.DOTALL) is not None:
            mylogger.error(f"Assembly code matching failed")
        # 提取sqtt文件pid
        pid_match = re.search(r"HIP_PROF:Analysis file thread_trace_.*?_se[0-7]_(\d+)", output)
        if not pid_match:
            mylogger.error("The analysis sqtt information was not printed")
            return False
        sqtt_pid = pid_match.group(1)
        # 提取--sqtt输出的文件列表
        files = [entry.name for entry in os.scandir(cwd) if entry.is_file() and entry.stat().st_size > 0]

        # 2、检测输出的指令以及统计文件
        # pmc_file = f"pmc_results_{sqtt_pid}.txt"
        # if not self.exist_file(""):
        #     check_result = False
        #     mylogger.error(f"The pmc file '{pmc_file}' has not been generated")
        if check_gen_files:
            # vega 20,zf,km有4个se;kme,bw有8个se
            se_num = 4 if self.dcu_info["gfx_arch"] in ["gfx906", "gfx926"] else 8
            for i in range(se_num):
                # 指令流文件: thread_trace_(核函数索引号)__核函数名称_se(SE下标)_进程号.json
                instruction_flow_file = re.compile(rf"^thread_trace_0__.*?_se{i}_{sqtt_pid}(_\d)?\.(json|html)$")
                # 指令统计文件: thread_trace_(核函数索引号)__核函数名称_se(SE下标)_进程号.stat.html
                instruction_statistics_file = re.compile(rf"^thread_trace_0__.*?_se{i}_{sqtt_pid}\.stat\.html$")
                for file_pattern in (instruction_flow_file, instruction_statistics_file):
                    if not any((file_pattern.match(f)) for f in files):
                        check_result = False
                        mylogger.error(f"{file_pattern} does not exist")
        return check_result




if __name__ == '__main__':
    out_put="""
                HIP_PROF:process id '27107'
            HIP_PROF:db file './hip-prof-27107.db'
            HIP_PROF:out put file 'result_27107'
            HIP_PROF:profiling './MatrixTranspose'
            HIP_PROF:process begin
            Device name Device 66a1
            PASSED!
            HIP_PROF:process end run totol cost:3(s)
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            HIP PROF:hip API statistics
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            |Name                                                           |Calls          |TotalDurationNs            |AverageNs                  |Percentage                 |
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            |hipMalloc                                                      |2              |17396768                   |8698384                    |45.8165992668825           |
            |hipMemcpy                                                      |2              |16461758                   |8230879                    |43.3541316130903           |
            |hipLaunchKernel                                                |1              |3932452                    |3932452                    |10.356612068417            |
            |hipFree                                                        |2              |150720                     |75360                      |0.396940273130304          |
            |__hipPopCallConfiguration                                      |1              |16060                      |16060                      |0.0422960508656627         |
            |__hipPushCallConfiguration                                     |1              |9090                       |9090                       |0.0239396701350482         |
            |hipGetDeviceProperties                                         |1              |3600                       |3600                       |0.009481057479227          |
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            |Total                                                          |10             |37970448                   |3797044                    |100.0                      |
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            HIP PROF:hip kernel statistics
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            |Name                                         |Pars                         |Calls          |TotalDurationNs            |AverageNs                  |Percentage     |
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            |matrixTranspose(float*, float*, int)         |(1,256,256),(1,4,4)          |1              |12674187                   |12674187                   |100.0          |
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            |Total                                        |                             |1              |12674187                   |12674187                   |100.0          |
            ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
            HIP PROF:dump json (20/20)
            HIP_PROF:finish
    """
    obj = BasePublic()
    print(obj.get_hip_trace_info_from_output(out_put))
